package vip.liux.backend.adapter.web;

import lombok.Getter;
import lombok.SneakyThrows;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import vip.liux.contracts.shared.enums.file.FileType;
import vip.liux.contracts.shared.kits.FileValidator;
import vip.liux.contracts.shared.properties.FilesProperties;
import vip.liux.contracts.shared.value.FileInfo;
import vip.liux.core.exception.SystemException;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.apache.commons.io.IOUtils.DIR_SEPARATOR_UNIX;

@RestController
@RequestMapping("/api/file-management/oss")
public class OssController {

    private static final Logger log = LoggerFactory.getLogger(OssController.class);
    private final FilesProperties filesProperties;

    public OssController(FilesProperties filesProperties) {
        this.filesProperties = filesProperties;
    }

    @PostMapping("/single-upload")
    public String singleUpload(MultipartFile file) {

        FileUpload fileUpload = new FileUpload(file);

        if (fileUpload.canUpload()) {
            FileUploadResult result = doFileUpload(fileUpload);
            return StringUtils.join(filesProperties.getDomain(), DIR_SEPARATOR_UNIX, result.url());
        }

        throw new SystemException("文件上传失败");
    }

    @PostMapping("/multi-upload")
    public List<String> multiUpload(List<MultipartFile> files) {
        // 示例代码，实际实现可以根据需求调整
        return files.stream()
                .map(this::singleUpload)
                .collect(Collectors.toList());
    }

    private FileUploadResult doFileUpload(FileUpload fileUpload) {
        Date now = new Date();
        String fileName = DateFormatUtils.format(now, "yyyyMMdd")
                + DIR_SEPARATOR_UNIX
                + fileUpload.getId()
                + "."
                + fileUpload.extensionName;
        // filename = 20210726/1234567890.jpg
        File destinationFile = new File(StringUtils.join(filesProperties.getLocalPath(), DIR_SEPARATOR_UNIX, fileName));
        mkdirIfNotExists(destinationFile);
        byte[] file = fileUpload.getFile();
        try {
            FileCopyUtils.copy(file, destinationFile);
        } catch (IOException e) {
            log.error("save file error", e);
            throw new SystemException(e);
        }

        return new FileUploadResult(fileUpload.getId(), fileName);
    }

    private void mkdirIfNotExists(File file) {
        // 检查指定文件的所有父级目录, 若不存在,则创建
        File parentFile = file.getParentFile();
        if (!parentFile.exists()) {
            if (!parentFile.mkdirs()) {
                throw new SystemException("创建目录失败");
            }
        }
    }

    @Getter
    public static class FileUpload implements Serializable, FileInfo {

        /**
         * 唯一标识
         */
        private final String id;

        /**
         * 文件名称
         */
        private final String name;

        /**
         * 文件格式名称
         */
        private final String extensionName;

        /**
         * 文件大小
         */
        private final long size;

        /**
         * 文件类型
         */
        private final FileType fileType;

        /**
         * MultipartFile
         */
        private transient final byte[] file;

        /**
         * 文件上传限制
         */
        private transient final FileValidator validator;

        @SneakyThrows
        public FileUpload(MultipartFile file) {
            Assert.notNull(file, "文件不能为空");
            byte[] bytes = FileCopyUtils.copyToByteArray(file.getInputStream());
            String extension = FilenameUtils.getExtension(file.getOriginalFilename());
            Assert.notNull(extension, "文件格式不正确");
            this.id = DigestUtils.md5Hex(bytes);
            this.file = bytes;
            this.name = file.getOriginalFilename();
            this.size = file.getSize();
            this.extensionName = extension;
            this.fileType = FileType.getFileType(extensionName);
            this.validator = this.fileType.getFileValidator();
        }

        public boolean canUpload() {
            return Optional.ofNullable(validator)
                    .map(fileUploadLimit -> validator.validate(this))
                    .orElseThrow(() -> new UnsupportedOperationException("FileUploadLimit is null"));
        }
    }

    public record FileUploadResult(String id, String url) {
    }
}
